using System.Collections.Generic;
using Gj.Galaxy.Logic;
using MessagePack;
using UnityEngine;

namespace Gj
{
    public class AreaMapContainer
    {
        private byte[] _content;
        private bool _edit;

        private AreaMapManager _manager;
        private readonly Dictionary<Vector3, AreaUnit> _units = new();


        public Vector3 Position;

        public static AreaMapContainer Init(AreaMapManager manager)
        {
            return Init(manager, Vector3.zero);
        }

        public static AreaMapContainer Init(AreaMapManager manager, Vector3 position)
        {
            var instance = new AreaMapContainer();
            instance.Position = position;
            instance._manager = manager;
            return instance;
        }

        public void Add(Vector3 position, Quaternion rotation, AreaMapLayer layer, string assets)
        {
            _edit = true;
            var layerIndex = _manager.GetLayerIndex(layer);
            var assetsIndex = layer.GetIndex(assets);
            _units.Add(position, new AreaUnit(this, layerIndex, assetsIndex, position, rotation));
        }

        public bool Remove(Vector3 position)
        {
            _edit = true;
            return _units.Remove(position);
        }

        public byte[] Encode()
        {
            if (_edit)
            {
                _edit = false;
                var units = new AreaInlineUnit[_units.Count];
                var e = _units.GetEnumerator();
                var i = 0;
                while (e.MoveNext())
                {
                    units[i] = e.Current.Value.Inline();
                    i++;
                }

                e.Dispose();
                _content = MessagePackSerializer.Serialize(units);
            }

            return _content;
        }

        private void Decode(byte[] content)
        {
            _edit = false;
            _content = content;
            _units.Clear();
            var units = MessagePackSerializer.Deserialize<AreaInlineUnit[]>(_content);
            for (var i = 0; i < units.Length; i++) _units.Add(units[i].position, new AreaUnit(this, ref units[i]));
        }

        public Dictionary<Vector3, AreaUnit>.Enumerator GetEnumerator()
        {
            return _units.GetEnumerator();
        }

        public static void AreaMap()
        {
            var o1 = new GameObject();
            var layer1 = o1.GetComponent<AreaMapLayer>();

            var m = new GameObject();
            var manager = m.GetComponent<AreaMapManager>();

            manager.Layers.Add(layer1);

            // 以上可以通过面板绑定，下面开始实际使用

            var container = Init(manager, Vector3.zero);

            // 添加建造物: 一个坐标只能存放一个
            container.Add(Vector3.zero, Quaternion.identity, layer1, "ccc");

            // 序列化：获取二进制
            var b = container.Encode();

            // 反序列化：得到数据
            var container1 = Init(manager, Vector3.zero);
            container1.Decode(b);
            var e = container.GetEnumerator();
            while (e.MoveNext()) Debug.Log(e.Current.Value);
            e.Dispose();

            // 移除数据
            container1.Remove(Vector3.zero);
        }

        public static void AreaDemo()
        {
            var m = new GameObject();
            var layer1 = m.GetComponent<AreaMapLayer>();
            var manager = m.GetComponent<AreaMapManager>();

            manager.Layers.Add(layer1);

            // 以上可以通过面板绑定，下面开始实际使用

            var container = Init(manager, Vector3.zero);
            AreaConnect.Connect("1.0.0", success =>
            {
                AreaConnect.Join("home", null);

                // 获取区域建造信息
                AreaConnect.Load(Vector3.zero, b => { container.Decode(b); });

                // 变更建筑信息
                AreaConnect.Edit(Vector3.zero, container.Encode());
            });
        }

        [MessagePackObject]
        public struct AreaInlineUnit
        {
            [Key(0)] public int layer;
            [Key(1)] public int index;
            [Key(2)] public Vector3 position;
            [Key(3)] public Quaternion rotation;
        }

        public class AreaUnit
        {
            private readonly AreaMapContainer _container;

            private readonly AreaInlineUnit _unit;

            public AreaUnit(AreaMapContainer container, int layer, int index, Vector3 position, Quaternion rotation)
            {
                _container = container;
                _unit = new AreaInlineUnit
                {
                    layer = layer,
                    index = index,
                    position = position,
                    rotation = rotation
                };
            }

            public AreaUnit(AreaMapContainer container, ref AreaInlineUnit unit)
            {
                _container = container;
                _unit = unit;
            }

            public Vector3 Position => _unit.position;

            public Quaternion Rotation => _unit.rotation;

            internal AreaInlineUnit Inline()
            {
                return _unit;
            }

            public Vector3 World()
            {
                return _container.Position + Position;
            }

            public string Assets()
            {
                return _container._manager.GetAssetsPath(_unit.layer, _unit.index);
            }
        }
    }
}